package ru.susu.algebra.field.quadratic;

import java.math.BigInteger;

/**
 * Основная единица квадратичного поля, является наследником {@link QuadraticInteger},
 * переопределяет часть методов в силу того, что она имеет вид a+bw, где
 * w = sqrt(d), либо w = (1+sqrt(d))/2, если d mod 4 == 1.
 *
 * @author akargapolov
 * @since: 21.04.2010
 */
public class QuadraticFieldGeneralForm extends QuadraticInteger
{
	private static BigInteger FOUR = BigInteger.valueOf(4);

	public QuadraticFieldGeneralForm()
	{
	}

	public QuadraticFieldGeneralForm(QuadraticFieldGeneralForm other)
	{
		super(other);
	}

	public QuadraticFieldGeneralForm(long a, long b, long d)
	{
		super(a, b, d);
	}

	public QuadraticFieldGeneralForm(BigInteger a, BigInteger b, BigInteger d)
	{
		super(a, b, d);
	}

	/**
	 * Произведение основных единиц. Отличается от основного произведения случаем d mod 4 == 1.
	 * В таком случае результат будет:
	 * a = a1*a2 + (d - 1)/4*b1*b2
	 * b = a1*b2 + a2*b1 + b1*b2
	 *
	 * @param multend второй операнд произведения
	 * @return результат произведения
	 */
	@Override
	public QuadraticInteger multiply(QuadraticInteger multend) throws Exception
	{
		checkEqualsD(multend);

		QuadraticInteger result;

		if (isSpecialCase(_d))
		{
			result = new QuadraticFieldGeneralForm(this);
			//a = a1*a2 + (d - 1)/4*b1*b2
			result._a = _a.multiply(multend._a);
			result._a = result._a.add(_d.subtract(BigInteger.ONE).divide(FOUR).multiply(_b).multiply(multend._b));

			//b = a1*b2 + a2*b1 + b1*b2
			result._b = _a.multiply(multend._b).add(multend._a.multiply(_b)).add(_b.multiply(multend._b));
		}
		else
		{
			result = super.multiply(multend);
		}

		return result;
	}

	/**
	 * Возвращает норму элемента. Обработка случая d mod 4 == 1, тогда
	 * norm(z) = a^2 + b^2 * (1 - d) / 4 + a * b
	 * @return норма элемента
	 */
	@Override
	public BigInteger norm() throws Exception
	{
		if (isSpecialCase(_d))
		{
			BigInteger first = _a.multiply(_a); //a*a
			BigInteger second = _a.multiply(_b); //a*b
			BigInteger third = _b.multiply(_b).multiply(BigInteger.ONE.subtract(_d).divide(FOUR)); //b*b*(1-d)/5
			return first.add(second).add(third);
		}
		else
		{
			return super.norm();
		}
	}

	/**
	 * Возвращает след элемента tr(a+bw) = 2a + b
	 * @return след элемента
	 */
	public BigInteger getTrace() throws Exception
	{
		return _a.multiply(BigInteger.valueOf(2)).add(_b);
	}

	@Override
	public String toString()
	{
		return new GeneralUnitFormatter().format(this);
	}

	/**
	 * @return сопряженный элемент квадратичного поля
	 */
	@Override
	public QuadraticInteger getConjugateNumber()
	{
		if (isSpecialCase(_d))
		{
			QuadraticFieldGeneralForm result = new QuadraticFieldGeneralForm(this);
			result._a = result._a.add(result._b);
			result._b = result._b.multiply(BigInteger.valueOf(-1));
			return result;
		}
		else
		{
			return super.getConjugateNumber();
		}
	}

	public static boolean isSpecialCase(BigInteger d)
	{
		return d.mod(FOUR).intValue() == 1;
	}
}
